
use common_uu::{arg, JsonVExentd};
use common_uu::file::FileExtend;
use common_uu::IResult;
use image::{open, GenericImage, GenericImageView, ImageBuffer, Rgba, RgbaImage};
use std::fs::File;
use std::path::Path;

/// 计算x坐标
/// hash_type [ ahash,dhash,phash ]
/// block_path 滑块
/// big_path   大图
pub fn calc_x(block_path: &str, big_path: &str) -> IResult<f64> {
    let dir_path = File::create_temporary_folder("captcha")?;

    //第一步，从滑块图中找到左上坐标，计算出x,y
    let (width, sw, sh, y, sub_png) =
        finding_small_images(block_path.to_string(), dir_path.clone())?;

    let hash_type = {
        match arg::get_arg("captcha_hash_type").as_string2() {
            Some(v) => v,
            None => String::from("dhash"),
        }
    };

    //计算滑块的hash
    let slider_hash = get_hash(&hash_type, sub_png.as_str());

    // 获得大图对象
    let mut big = open(big_path)?;
    let big_width = big.width();

    // 返回值
    let mut min_x = 0;
    let mut min_dist = std::u64::MAX;

    //跳过滑块宽度
    let top = y;
    for x in width..big_width - sw {
        // 切割一小块图片进行逐块比对
        let clip = big.sub_image(x, top, sw, sh).to_image();

        // 保存每一块小图到临时文件夹
        let mut clip_path = dir_path.clone();
        let clip_str = &format!("/_clip({}).png", x);
        clip_path.push_str(clip_str);
        // let clip_path = &format!("out/clip{}.png", x);
        clip.save(clip_path.clone())?;

        // 计算每个小块的hash值
        let clip_hash = get_hash(&hash_type, clip_path.clone());

        // 对比滑块与切割小图的hash之间的差距, 找到差距最小的那一个即为理论上的最优解
        let dist = pihash::get_hamming_distance(slider_hash, clip_hash);
        if dist < min_dist {
            min_dist = dist;
            min_x = x;
        }
    }

    // 计算完删除文件
    std::fs::remove_dir_all(dir_path)?;

    Ok(min_x as f64 / big_width as f64)
}

///绘制图片
fn draw_image(
    x: i32,
    y: i32,
    target: &mut ImageBuffer<Rgba<u8>, Vec<u8>>,
    image: &ImageBuffer<Rgba<u8>, Vec<u8>>,
) {
    let (target_width, target_height) = (target.width() as i32, target.height() as i32);

    let (x, y, width, height) = (x, y, image.width() as i32, image.height() as i32);

    /* 颜色混合
        一般数学计算中，颜色取值是：R,G,B∈[0,255],A∈[0,1]
        所以对于一般的颜色混合有：Color(RGBA)=Color(R1,G1,B1,A1)+Color(R2,G2,B2,A2)

        标准的颜色混合算法如下：
        A=1−(1−α1)∗(1−α2)

        R=(α1*R1+(1−α1)*α2*R2)/A
        G=(α1*G1+(1−α1)*α2*G2)/A
        B=(α1*B1+(1−α1)*α2*B2)/A
    */
    let mut sy = 0;
    for cy in y..y + height {
        let mut sx = 0;
        for cx in x..x + width {
            if cx >= 0 && cx < target_width && cy >= 0 && cy < target_height {
                let image_pixel = image[(sx, sy)];
                let (r1, g1, b1, a1) = (
                    image_pixel[0] as f64,
                    image_pixel[1] as f64,
                    image_pixel[2] as f64,
                    image_pixel[3] as f64 / 255.0,
                );
                let pixel = &mut target[(cx as u32, cy as u32)];
                let (r2, g2, b2, a2) = (
                    pixel[0] as f64,
                    pixel[1] as f64,
                    pixel[2] as f64,
                    pixel[3] as f64 / 255.0,
                );

                let a = 1.0 - (1.0 - a1) * (1.0 - a2);
                let r = (a1 * r1 + (1.0 - a1) * a2 * r2) / a;
                let g = (a1 * g1 + (1.0 - a1) * a2 * g2) / a;
                let b = (a1 * b1 + (1.0 - a1) * a2 * b2) / a;

                pixel[0] = r as u8;
                pixel[1] = g as u8;
                pixel[2] = b as u8;
                pixel[3] = (a * 255.0) as u8;
            }
            sx += 1;
        }

        sy += 1;
    }
}

/// 获得hash值
fn get_hash(hash_type: &str, sub_png: impl Into<String>) -> u64 {
    let sub_path = sub_png.into();
    let sub_path = Path::new(&sub_path);
    let pi = pihash::PIHash::new(None);
    let sub_hash = {
        if hash_type == "ahash" {
            pi.get_ahash(sub_path)
        } else if hash_type == "dhash" {
            pi.get_dhash(sub_path)
        } else if hash_type == "phash" {
            pi.get_phash(sub_path)
        } else {
            0u64
        }
    };
    sub_hash
}

/// 寻找小图
fn finding_small_images(
    block_path: String,
    dir_path: String,
) -> IResult<(u32, u32, u32, u32, String)> {
    let mut block = open(block_path)?;
    //从上往下，从左往右，寻找第一个非透明像素点（左上角）
    let mut left_top = None;
    for x in 0..block.width() {
        for y in 0..block.height() {
            let pixel: Rgba<u8> = block.get_pixel(x, y);
            if pixel[3] > 30 {
                left_top = Some((x, y));
                break;
            }
        }
        if left_top.is_some() {
            break;
        }
    }
    let left_top = left_top.ok_or("left_top id none")?;
    // println!("左上角{:?}", left_top);
    let (sw, sh) = (32, 22);
    //y+4, x+6, 取宽高=(sw,sh)
    let (x, y) = (left_top.0 + 3, left_top.1 + 3);
    let mut sub: RgbaImage = block.sub_image(x, y, sw, sh).to_image();
    //添加40%白色半透明
    let mut mask: RgbaImage = RgbaImage::new(sw, sh);
    for pixel in mask.pixels_mut() {
        pixel[0] = 255;
        pixel[1] = 255;
        pixel[2] = 255;
        pixel[3] = 102;
    }
    draw_image(0, 0, &mut sub, &mask);
    let p = dir_path + "/sub.png";
    sub.save(p.clone())?;
    Ok((block.width(), sw, sh, y, p))
}
